﻿// ---------------------------------------------------------------------------------------------------------------------
//   Copyright (c) SRT Solutions 2009. All rights reserved.
// ---------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NUnit.Framework;

namespace SRTSolutions.Elevate.Guide
{
    public class LazyListsWithoutCaching
    {
        [Test]
        public void GetValuesFromAnIndexingFunction()
        {
            //given a function that generates a value based on an index
            Func<int, string> indexer = index => index.ToString();

            //we can create a lazy list without caching
            IList<string> list = new LazyListWithoutCaching<string>(indexer, 100);

            //the values in the list will be populated by applying the indexer function
            Assert.AreEqual("1", list[1]);
            Assert.AreEqual("2", list[2]);
            Assert.AreEqual("65", list[65]);
        }

        [Test]
        public void SetValuesWithASetterFunction()
        {
            // Construct a lazy list with a setter function
            var list = GetLazyListWithoutCachingWithSetter();

            // Verify the values before modification.
            Assert.AreEqual("1", list[1]);
            Assert.AreEqual("2", list[2]);
            Assert.AreEqual("45", list[45]);

            // Modify the values in the list.
            list[1] = "2";
            list[2] = "1";
            list[45] = "99";

            // Verify the values changed
            Assert.AreEqual("2", list[1]);
            Assert.AreEqual("1", list[2]);
            Assert.AreEqual("99", list[45]);
        }

        [Test]
        public void GetCountFromAnInputArgument()
        {
            //we can construct a lazy list without caching as above
            //this time focus on the second arguemnt, the initial of the list
            Func<int, string> indexer = index => index.ToString();
            IList<string> list = new LazyListWithoutCaching<string>(indexer, 100);

            Assert.AreEqual(100, list.Count);
            Assert.Throws<ArgumentOutOfRangeException>(() => { var tmp = list[100]; });
        }

        [Test]
        public void AlwaysCallsTheIndexerToGetAValue()
        {
            //given a lazy list without caching from an indexer function that lets us keep track of how
            //many times it is called
            var calledCount = 0;
            Func<int, string> indexer = index => { calledCount++; return index.ToString(); };
            var list = new LazyListWithoutCaching<string>(indexer, 50);

            //when we index the list
            var result = list[20];

            //the indexer function gets called once per index that we access
            Assert.AreEqual(1, calledCount);
            result = list[10];
            Assert.AreEqual(2, calledCount);
            result = list[16];
            Assert.AreEqual(3, calledCount);

            //even if we get the same value more than once
            var result2 = list[16];
            Assert.AreEqual(4, calledCount);

            result2 = list[16];
            Assert.AreEqual(5, calledCount);

            Assert.AreEqual(result, result2);
        }

        public IList<string> GetLazyListWithoutCaching()
        {
            Func<int, string> indexer = index => index.ToString();
            return new LazyListWithoutCaching<string>(indexer, 100);
        }

        public IList<string> GetLazyListWithoutCachingWithSetter()
        {
            // Generate a backing store list of strings
            var stringList = 0.Through(99).Select(index => index.ToString()).ToList();

            // given a function that indexes our backing store 
            Func<int, string> indexer = index => stringList[index];

            // given a function that sets values on our backing store
            Action<int, string> setter = (index, value) => stringList[index] = value;

            //we can create a lazy list without caching
            return new LazyListWithoutCaching<string>(indexer, setter, stringList.Count);
        }

        [Test]
        public void IsReadOnlyWithNoSetter()
        {
            //because no setter function was given,
            //this list is read only
            var list = GetLazyListWithoutCaching();

            Assert.IsTrue(list.IsReadOnly);
        }

        [Test]
        public void IsNotReadOnlyWithSetter()
        {
            //because a setter function was given,
            //this list is not read only
            var list = GetLazyListWithoutCachingWithSetter();

            Assert.IsFalse(list.IsReadOnly);
        }

        #region UnitTests

        [Test]
        public void GetWithNegativeIndex()
        {
            var list = GetLazyListWithoutCaching();
            Assert.Throws<ArgumentOutOfRangeException>(() => { var tmp = list[-1]; });
        }

        [Test]
        public void SetWithNegativeIndex()
        {
            var list = GetLazyListWithoutCachingWithSetter();
            Assert.Throws<ArgumentOutOfRangeException>(() => { list[-1] = "-1"; });
        }

        [Test]
        public void CountNegative()
        {
            Assert.Throws<ArgumentOutOfRangeException>(() => new LazyListWithoutCaching<int>(x => x, -1));
        }

        [Test]
        public void NullIndexer()
        {
            Assert.Throws<ArgumentNullException>(() => new LazyListWithoutCaching<int>(null, 100));
        }

        [Test]
        public void EmptyList()
        {
            var list = new LazyListWithoutCaching<int>(x => x, 0);
            Assert.Throws<ArgumentOutOfRangeException>(() => { var tmp = list[0]; });
        }

        [Test]
        public void IndexOf()
        {
            var calledCount = 0;
            Func<int, string> indexer = index => { calledCount++; return index.ToString(); };
            var list = new LazyListWithoutCaching<string>(indexer, 50);

            Assert.AreEqual(15, list.IndexOf("15"));
            Assert.Greater(calledCount, 0);
        }

        [Test]
        public void IndexOfFail()
        {
            var calledCount = 0;
            Func<int, string> indexer = index => { calledCount++; return index.ToString(); };
            var list = new LazyListWithoutCaching<string>(indexer, 50);

            Assert.AreEqual(-1, list.IndexOf("not in the list"));
            Assert.AreEqual(50, calledCount);
        }

        [Test]
        public void Insert()
        {
            var list = GetLazyListWithoutCaching();

            Assert.Throws<NotSupportedException>(() => list.Insert(0, "epic insert fail"));
        }

        [Test]
        public void RemoveAt()
        {
            var list = GetLazyListWithoutCaching();

            Assert.Throws<NotSupportedException>(() => list.RemoveAt(0));
        }

        [Test]
        public void SetWithNoSetterFunction()
        {
            var list = GetLazyListWithoutCaching();

            Assert.Throws<NotSupportedException>(() => list[0] = "No setter was supplied");
        }

        [Test]
        public void Add()
        {
            var list = GetLazyListWithoutCaching();

            Assert.Throws<NotSupportedException>(() => list.Add("epic add fail"));
        }

        [Test]
        public void Clear()
        {
            var list = GetLazyListWithoutCaching();

            Assert.Throws<NotSupportedException>(() => list.Clear());
        }

        [Test]
        public void Contains()
        {
            var calledCount = 0;
            Func<int, string> indexer = index => { calledCount++; return index.ToString(); };
            var list = new LazyListWithoutCaching<string>(indexer, 50);

            Assert.IsTrue(list.Contains("20"));
            Assert.Greater(calledCount, 0);
        }

        [Test]
        public void ContainsFail()
        {
            var calledCount = 0;
            Func<int, string> indexer = index => { calledCount++; return index.ToString(); };
            var list = new LazyListWithoutCaching<string>(indexer, 50);

            Assert.IsFalse(list.Contains("not in the list"));
            Assert.AreEqual(50, calledCount);
        }

        [Test]
        public void CopyTo()
        {
            var calledCount = 0;
            Func<int, string> indexer = index => { calledCount++; return index.ToString(); };
            var list = new LazyListWithoutCaching<string>(indexer, 50);

            var expected = 50.Times(x => x.ToString());
            var result = new string[70];
            list.CopyTo(result, 20);
            Assert.AreEqual(expected, result.Skip(20));
            Assert.AreEqual(50, calledCount);
        }

        [Test]
        public void Remove()
        {
            var list = GetLazyListWithoutCaching();

            Assert.Throws<NotSupportedException>(() => list.Remove("Epic Remove Fail"));
        }

        [Test]
        public void NonGenericGetEnumerator()
        {
            var list = GetLazyListWithoutCaching();

            var fromUntyped = ((IEnumerable)list).Cast<string>();
            Assert.AreEqual(list, fromUntyped);
        }

        #endregion
    }
}
